package com.sixbro.authorization.api;

import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.sixbro.authorization.security.domain.OAuth2UserDetails;
import com.sixbro.authorization.util.SpringSecurityHolder;
import com.sixbro.common.domain.ApiResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
import org.springframework.security.oauth2.provider.token.ConsumerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.util.StringUtils;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;

import java.security.KeyPair;
import java.security.Principal;
import java.security.interfaces.RSAPublicKey;
import java.util.Map;

/**
 * <p>
 *
 * </p>
 *
 * @author: Mr.Lu
 * @since: 2021/6/15 17:54
 */
@Slf4j
@RestController
@RequestMapping("/oauth")
@AllArgsConstructor
@Api(tags = "认证中心", description = "认证中心登录认证")
public class OAuth2Controller {

    private final KeyPair keyPair;
    private final TokenStore tokenStore;
    private final TokenEndpoint tokenEndpoint;
    private final ConsumerTokenServices consumerTokenServices;
    private final UserDetailsService userDetailsService;

    @ApiOperation(value = "OAuth2认证", notes = "login")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "grant_type", defaultValue = "password", value = "授权模式", required = true),
            @ApiImplicitParam(name = "client_id", defaultValue = "client", value = "Oauth2客户端ID", required = true),
            @ApiImplicitParam(name = "client_secret", defaultValue = "123456", value = "Oauth2客户端秘钥", required = true),
            @ApiImplicitParam(name = "refresh_token", value = "刷新token"),
            @ApiImplicitParam(name = "username", defaultValue = "admin", value = "登录用户名"),
            @ApiImplicitParam(name = "password", defaultValue = "123456", value = "登录密码")
    })
    @PostMapping("/token")
    public Object postAccessToken(
            @ApiIgnore Principal principal,
            @ApiIgnore @RequestParam Map<String, String> parameters
    ) throws HttpRequestMethodNotSupportedException {
        OAuth2AccessToken oAuth2AccessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody();
        return ApiResponse.success(oAuth2AccessToken);
    }

    /**
     * 退出并删除token
     * @param access_token
     * @return
     */
    @ApiOperation(value = "注销", notes = "logout")
    @DeleteMapping("/logout")
    public ApiResponse<?> logout(@RequestHeader(value = HttpHeaders.AUTHORIZATION, required = true) String access_token) {
        if (StringUtils.hasLength(access_token)) {
            return ApiResponse.success();
        }
        String token = access_token.replace(OAuth2AccessToken.BEARER_TYPE, "").trim();
        return ApiResponse.success(consumerTokenServices.revokeToken(token));
    }

    /**
     * 更新用户信息时更新redis中的用户信息
     * @param authentication
     * @return java.lang.String
     */
    @GetMapping("/update")
    @ResponseBody
    public String updateCacheUserInfo(Authentication authentication) {
        if (authentication instanceof OAuth2Authentication) {
            OAuth2Authentication auth2Authentication = (OAuth2Authentication) authentication;
            Authentication userAuthentication = auth2Authentication.getUserAuthentication();
            OAuth2Authentication newOAuth2Authentication = null;
            if (userAuthentication instanceof UsernamePasswordAuthenticationToken) {
                OAuth2UserDetails userDetails = (OAuth2UserDetails) userDetailsService.loadUserByUsername("yaohw");
                userDetails.setMobile("yaohw2");
                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities());
                newOAuth2Authentication = new OAuth2Authentication(auth2Authentication.getOAuth2Request(),usernamePasswordAuthenticationToken);
            }
            OAuth2AccessToken accessToken = tokenStore.getAccessToken(auth2Authentication);
            if (newOAuth2Authentication != null) {
                tokenStore.storeAccessToken(accessToken,newOAuth2Authentication);
            }
        }
        return "ok";
    }

    /**
     * 令牌管理调用
     * @param refreshToken
     * @return
     */
    @DeleteMapping("/{token}")
    public ApiResponse<Boolean> removeToken(@PathVariable("token") String refreshToken) {
        // 清空 refresh token
        OAuth2RefreshToken auth2RefreshToken = tokenStore.readRefreshToken(refreshToken);
        if (auth2RefreshToken == null || StringUtils.isEmpty(auth2RefreshToken.getValue())) {
            return ApiResponse.success();
        }
        tokenStore.removeAccessTokenUsingRefreshToken(auth2RefreshToken);
        tokenStore.removeRefreshToken(auth2RefreshToken);
        return ApiResponse.success();
    }

    @ApiOperation(value = "获取公钥", notes = "login")
    @GetMapping("/rsa/public-key")
    public Map<String, Object> getPublicKey() {
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAKey key = new RSAKey.Builder(publicKey).build();
        return new JWKSet(key).toJSONObject();
    }

    @GetMapping("/current/user")
    public ApiResponse currentUser() {
        OAuth2UserDetails user = SpringSecurityHolder.getUser();
        return ApiResponse.success(user);
    }
}
